package org.study.dsm.ds.config;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.google.common.collect.Maps;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.study.dsm.ds.mapper.DataSourceInfoMapper;
import org.study.dsm.ds.model.DataSourceInfo;

import javax.sql.DataSource;
import java.sql.*;
import java.util.*;

/**
 * 加载数据库所有数据源。
 * 参考 https://nanxiang.blog.csdn.net/article/details/131245273
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2024年04月27日
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class DataSourceChangeProvider {
    private final DataSource dataSource;
    private final DynamicRoutingDataSource dynamicRoutingDataSource;
    private final DefaultDataSourceCreator dataSourceCreator;
    private final DataSourceInfoMapper dataSourceInfoMapper;

    @PostConstruct
    public void initDataSource() {
        List<DataSourceInfo> dataSourceInfoList = dataSourceInfoMapper.selectList(null);
        if (CollectionUtils.isNotEmpty(dataSourceInfoList)) {
            // 数据源操作，也可以加入不同的数据源类型
            for (DataSourceInfo dataSourceInfo : dataSourceInfoList) {
                this.addDataSource(dataSourceInfo);
            }
        }
    }

    /**
     * 增加数据源
     *
     * @param dataSourceInfo 数据源信息
     */
    public void addDataSource(DataSourceInfo dataSourceInfo) {
        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        String dsName = dataSourceInfo.getDsKey();
        dataSourceProperty.setPoolName(dsName);
        // dataSourceProperty.setDriverClassName("com.mysql.cj.jdbc.Driver");
        Integer typeCode = dataSourceInfo.getTypeCode();
        String host = dataSourceInfo.getHost();
        String port = dataSourceInfo.getPort();
        String dbName = dataSourceInfo.getDbName();
        dataSourceProperty.setDriverClassName(DatasourceTypeEnum.getDriverClassName(typeCode));
        // dataSourceProperty.setUrl("jdbc:" + DatasourceTypeEnum.getDsTypeEnum(typeCode).getSubProtocol() + "://" + host + ":" + port + "/" + dbName);
        dataSourceProperty.setUrl(this.parseDbUrl(DatasourceTypeEnum.getUrlPattern(typeCode), host, port, dbName));
        dataSourceProperty.setUsername(dataSourceInfo.getUsername());
        dataSourceProperty.setPassword(dataSourceInfo.getPassword());
        DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
        dynamicRoutingDataSource.addDataSource(dsName, dataSource);
    }

    /**
     * 移除数据源
     *
     * @param dsKey 数据源key
     */
    public void removeDataSource(String dsKey) {
        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
        dynamicRoutingDataSource.removeDataSource(dsKey);
    }

    /**
     * 查看数据源是否存在
     *
     * @param dsKey 数据源key
     */
    public boolean existsDataSource(String dsKey) {
        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
        DataSource existsDataSource = dynamicRoutingDataSource.getDataSource(dsKey);
        // return existsDataSource != null;
        return dynamicRoutingDataSource.getDataSources().containsKey(dsKey);
    }

    /**
     * 检测数据源是否正确
     *
     * @param dsKey 数据源key
     */
    public boolean checkDataSource(String dsKey) {
        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
        DataSource existsDataSource = dynamicRoutingDataSource.getDataSource(dsKey);
        if (existsDataSource != null) {
            try (Connection connection = existsDataSource.getConnection();
                 Statement statement = connection.createStatement()) {
                statement.execute("SELECT 1");
                return true;
            } catch (SQLException e) {
                return false;
            }
        }
        return false;
    }

    /**
     * 获取所有数据源
     */
    public Set<String> getDataSource() {
        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
        Map<String, DataSource> dataSourceMap = dynamicRoutingDataSource.getDataSources();
        return dataSourceMap.keySet();
    }

    public Map<String, String> getDataSourceStatus() {
        Map<String, DataSource> dataSources = dynamicRoutingDataSource.getDataSources();
        Map<String, String> details = Maps.newHashMap();
        for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
            String dsName = entry.getKey();
            DataSource currentDs = entry.getValue();
            boolean isHealthy = false;
            try (Connection connection = currentDs.getConnection()) {
                DatabaseMetaData metaData = connection.getMetaData();
                String productName = metaData.getDatabaseProductName();
                isHealthy = connection.isValid(3);
            } catch (Exception e) {
                // 连接失败
            }
            details.put(dsName, isHealthy ? "UP" : "DOWN");
        }
        return details;
    }

    private String parseDbUrl(String url, String host, String port, String dbName) {
        Map<String, String> paramMap = new HashMap<>() {{
            put("host", host);
            put("port", port);
            put("db_name", dbName);
        }};
        for (Map.Entry<String, String> entry : paramMap.entrySet()) {
            url = StringUtils.replace(url, "${" + entry.getKey() + "!}", entry.getValue());
        }
        return url;
    }
}
